package com.job51.hro.datacheckservice.service;

import com.job51.common.util.ZipUtils;
import com.job51.hro.datacheckservice.dto.MailAttachment;
import com.job51.hro.datacheckservice.dto.config.LogScaner;
import com.job51.hro.datacheckservice.dto.config.Mail;
import com.job51.hro.datacheckservice.dto.config.WechatRobot;
import com.job51.hro.datacheckservice.dto.fegin.wechatrobot.FileUploadResult;
import com.job51.hro.datacheckservice.dto.fegin.wechatrobot.Media;
import com.job51.hro.datacheckservice.dto.fegin.wechatrobot.Publisher;
import com.job51.hro.datacheckservice.dto.fegin.wechatrobot.Text;
import com.job51.hro.datacheckservice.dto.xxjob.CustomErrorMail;
import com.job51.hro.datacheckservice.feign.WechatRobotActions;
import com.job51.hro.datacheckservice.feign.WechatRobotFileUploader;
import com.job51.hro.datacheckservice.util.DateUtil;
import com.job51.hro.datacheckservice.util.FileUtil;
import com.job51.hro.datacheckservice.util.ListUtil;
import com.xxl.job.core.log.XxlJobLogger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.mock.web.MockMultipartFile;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author pengfei.ma
 * @since 1.0.0
 */
@Service
public class ErrorMailServiceImpl implements ErrorMailService {

    @Resource
    LogScaner logScaner;

    @Resource
    JavaMailSenderImpl sender;

    @Resource
    WechatRobotActions wechatRobotActions;

    @Resource
    WechatRobotFileUploader wechatRobotFileUploader;

    @Resource
    WechatRobot wechatRobot;


    static final Logger log = LoggerFactory.getLogger(ErrorMailService.class);


    @Override
    public boolean sendLogMail(CustomErrorMail customErrorMail) {
        XxlJobLogger.log("准备邮件发送内容");
        Map<String, String> serviceText = new HashMap<>();
        boolean sendRobot = false;
        boolean withAttach = false;
        boolean sendCustomMail = true;
        boolean sendText = true;
        String sendIfNoText = "默认填充";
        MailAttachment mailAttachment = null;
        String zipName = "logs-collection";
        Map<String, List<String>> totalWorkPath = logScaner.getScanpath();
        List<String> serviceListen = new ArrayList<>(totalWorkPath.keySet());
        //xxjob主动取日志，支持一个服务一取
        if (customErrorMail.getServiceName().length() > 0) {
            if (!serviceListen.contains(customErrorMail.getServiceName())) {
                XxlJobLogger.log(String.format("自定义扫描服务:%s 未配置", customErrorMail.getServiceName()));
                return false;
            }
            serviceText.put(customErrorMail.getServiceName(), "客户自定义日志查询");
            List<String> logPaths;
            if (customErrorMail.getLogPath().isEmpty()) {
                Map<String, List<String>> workPath = logScaner.getScanpath();
                if (workPath.isEmpty()) {
                    log.error("发送自定义日志邮件失败：系统配置项logscan.scanpath不能为空");
                    return false;
                }
                logPaths = workPath.get(customErrorMail.getServiceName());
            } else {
                logPaths = customErrorMail.getLogPath();
            }
            mailAttachment = this.zipLogFile(zipName, logPaths);
            if (mailAttachment.getAttachment() != null) {
                withAttach = true;
            } else {
                sendCustomMail = false;
            }
        } else {
            // 抓取所有服务一定时间的日志
            serviceText = this.buildMailTexts();
            sendRobot = true;
        }
        Map<String, Mail> mailInfo = logScaner.getMail();
        if (serviceText == null) {
            XxlJobLogger.log("构建内容时出现异常情况，退出任务");
            return false;
        }
        for (String serviceName : serviceText.keySet()) {
            if (serviceText.get(serviceName) == null || "".equals(serviceText.get(serviceName).trim()) || !sendCustomMail) {
                XxlJobLogger.log(serviceName + "邮件内容为空，无需发送");
                continue;
            }
            if (serviceText.get(serviceName).length() > logScaner.getSizeToZip()) {
                withAttach = true;
                sendText = false;
                sendIfNoText = "日志较多,请即时处理orz";
                mailAttachment = zipLongText(serviceName + "-" + com.job51.common.util.date.DateUtil.format(new Date(), "MMdd-HHmm"), serviceText.get(serviceName));
            }
            MimeMessage mimeMessage = sender.createMimeMessage();
            MimeMessageHelper helper;
            String[] to;
            String[] cc = {};
            String subject;
            if (customErrorMail.getTo().isEmpty()) {
                to = mailInfo.get(serviceName).getTo();
                cc = mailInfo.get(serviceName).getCc();
            } else {
                to = customErrorMail.getTo().toArray(new String[0]);
                if (!customErrorMail.getCc().isEmpty()) {
                    cc = customErrorMail.getCc().toArray(new String[0]);
                }
            }
            if (customErrorMail.getSubject().length() == 0) {
                subject = mailInfo.get(serviceName).getSubject();
            } else {
                subject = customErrorMail.getSubject();
            }
            try {
                helper = new MimeMessageHelper(mimeMessage, true);
                helper.setFrom(mailInfo.get(serviceName).getFrom());
                helper.setTo(to);
                helper.setCc(cc);
                helper.setSubject(subject);
                if (withAttach) {
                    helper.addAttachment(mailAttachment.getFileName(), mailAttachment.getAttachment());
                }
                if (sendText) {
                    helper.setText(serviceText.get(serviceName));
                } else {
                    helper.setText(sendIfNoText);
                }
                sender.send(mimeMessage);
                //删除附件目录
                if (withAttach) {
                    FileUtil.delFileAndDir(mailAttachment.getFilePath());
                    withAttach = false;
                    sendText = true;
                }
            } catch (MessagingException e) {
                log.error("发送日志检查邮件失败:" + "To:" + Arrays.toString(to) + ",Subject:" + subject);
            }
            //增加robot发群聊的判断
            if (wechatRobot.getLogWatcher().contains(serviceName) && sendRobot) {
                String robotSubject = mailInfo.get(serviceName).getSubject();
//                this.sendRobotText(robotSubject);
                this.sendRobotFile(robotSubject, robotSubject + "_" + DateUtil.getString("yyyy-MM-dd_HH_mm_ss") + "_error.txt", serviceText.get(serviceName));
            }
            XxlJobLogger.log(serviceName + "邮件已发送");
        }
        return true;
    }


    private Boolean emptyCheck(String filteredText) {
        String emptyCheck = filteredText.replace("\n", "");
        emptyCheck = emptyCheck.replace(" ", "");
        emptyCheck = emptyCheck.replace("\r", "");
        return emptyCheck.length() == 0;
    }

    private MailAttachment zipLongText(String zipName, String text) {
        String uid = UUID.randomUUID().toString();
        String delPath = "/tmp/longLogZip_" + uid;
        File fileForZip = new File(delPath);
        boolean res = fileForZip.mkdirs();
        if (!res) {
            log.error("生成压缩文件路径失败：" + delPath);
        }
        String filePath = delPath + "/" + zipName + ".txt";
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(filePath);
            fileOutputStream.write(text.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("创建文件失败：" + filePath);
        } finally {
            try {
                fileOutputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        ZipUtils.zip(filePath);
        String zipPath = delPath + "/" + zipName + ".zip";
        File zipFile = new File(zipPath);
        MailAttachment mailAttachment = new MailAttachment();
        mailAttachment.setFilePath(delPath);
        mailAttachment.setAttachment(zipFile);
        mailAttachment.setFileName(zipName + ".zip");
        return mailAttachment;
    }

    private MailAttachment zipLogFile(String zipName, List<String> logPaths) {
        String uid = UUID.randomUUID().toString();
        String delPath = "/tmp/logZip_" + uid;
        String zipPath = delPath + "/" + zipName;
        File fileForZip = new File(zipPath);
        boolean res = fileForZip.mkdirs();
        if (!res) {
            log.error("生成压缩文件失败：" + zipPath);
        }
        //赋值日志文件到临时压缩目录
        for (String logPath : logPaths) {
            File originFile = new File(logPath);
            if (!originFile.exists()) {
                continue;
            }
            String tmpFilePath = zipPath + "/" + FileUtil.getFileName(logPath);
            try {
                FileUtils.copyFile(originFile, new File(tmpFilePath));
            } catch (IOException e) {
                log.error("拷贝文件到临时压缩目录失败:" + logPath);
            }
        }
        //压缩
        ZipUtils.zip(zipPath);
        MailAttachment mailAttachment = new MailAttachment();
        File zipFileEmptyCheck = new File(zipPath);
        if (zipFileEmptyCheck.list() == null || Objects.requireNonNull(zipFileEmptyCheck.list()).length <= 0) {
            return mailAttachment;
        }
        File zipFile = new File(zipPath + ".zip");
        mailAttachment.setAttachment(zipFile);
        mailAttachment.setFileName(zipName + ".zip");
        mailAttachment.setFilePath(delPath);
        return mailAttachment;
    }


    private Map<String, String> buildMailTexts() {
        Map<String, String> result = new HashMap<>();
        Map<String, List<String>> workPath = logScaner.getScanpath();
        if (workPath.isEmpty()) {
            log.error("配置项logscan.scanpath不能为空");
            return null;
        }

        //提前过滤非法路径
        for (String key : workPath.keySet()) {
            List<String> paths = new ArrayList<>(workPath.get(key));
            List<String> tmpPaths = new ArrayList<>(workPath.get(key));
            paths.forEach(path -> {
                String newPath = FileUtil.specialSignReplace(path);
                File file = new File(newPath);
                if (!file.isFile() || !file.exists()) {
                    tmpPaths.remove(path);
                } else {
                    tmpPaths.remove(path);
                    tmpPaths.add(newPath);
                }
            });
            StringBuilder text = new StringBuilder();
            for (String tmpPath : tmpPaths) {
                try {
                    text.append(this.buildMailText(tmpPath));
                    //filter过滤、
                    String filteredText = this.textFilter(text.toString());
                    if (this.emptyCheck(filteredText)) {
                        continue;
                    }
                    result.put(key, filteredText);
                } catch (Exception e) {
                    log.error("service: " + key + " path: " + tmpPath + " 添加文本内容时出错：" + e.getMessage());
                }
            }
        }
        return result;
    }

    private String buildMailText(String path) throws Exception {
        String startPattern = "^(?:Date:\\[)?([0-9]{4}-([0]{1}[1-9]{1}|[1]{1}[0-2]{1})-([0]{1}[1-9]{1}|[1-2]{1}[0-9]{1}|[3]{1}[0-1]{1}) ([0-1]{1}[0-9]{1}|[2]{1}[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1})(.[0-9]{3}])?";
//        String Pattern = "^[0-9]{4}-([0]{1}[1-9]{1}|[1]{1}[0-2]{1})-([0]{1}[1-9]{1}|[1-2]{1}[0-9]{1}|[3]{1}[0-1]{1}) ([0-1]{1}[0-9]{1}|[2]{1}[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}";
        String endPattern = "^(?:Date:\\[)?([0-9]{4}-([0]{1}[1-9]{1}|[1]{1}[0-2]{1})-([0]{1}[1-9]{1}|[1-2]{1}[0-9]{1}|[3]{1}[0-1]{1}) ([0-1]{1}[0-9]{1}|[2]{1}[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1})(.[0-9]{3}])?|(.controller.)";
        long secondTime = DateUtil.getSecondTimestamp(new Date());
        String timeFormat = "yyyy-MM-dd HH:mm:ss";
        int[] startPatternGroupNum = {0, 1};
        int[] endPatternGroupNum = {0, 1};
        return FileUtil.readTextWithPattern(path, startPattern, startPatternGroupNum, a -> {
            try {
                long findTime = DateUtil.getSecondTimestamp(a, timeFormat);
                return findTime <= secondTime && findTime > secondTime - logScaner.getTimegap();
            } catch (Exception e) {
                return false;
            }
        }, endPattern, endPatternGroupNum, (b, text, obj) -> {
            try {
                if (b.equals(".controller.")) {
                    return -1; //表示多输出1行 -n 输出 n行
                }
                long findTime = DateUtil.getSecondTimestamp(b, timeFormat);
                boolean timeCheck = findTime > secondTime || findTime <= secondTime - logScaner.getTimegap();
                if (timeCheck) {
                    return 0;
                }
                return 1;
            } catch (Exception e) {
                return 1;
            }
        });
    }


    private void sendRobotText(String subject) {
        Publisher publisher = new Publisher();
        publisher.setMsgtype("text");
        publisher.setText(new Text().setContent("【" + subject + "】：\n" + ListUtil.randomStr(this.getPoems()) + "\n\n").setMentioned_list(Collections.singletonList("@all")));
        wechatRobotActions.publish(publisher);
    }

    private void sendRobotFile(String subject, String fileName, String content) {
        String filePath = "/tmp/robotfile_" + UUID.randomUUID();
        FileUtil.createAndWriteContent(filePath, fileName, content);
        File file = new File(filePath + "/" + fileName);
        if (file.length() > 19 * 1024 * 1024) {
            ZipUtils.zip(file);
            fileName = FileUtil.getFileNameNoExt(fileName) + ".zip";
            file = new File(filePath + "/" + fileName);
        }
        MultipartFile cMultiFile;
        try {
            cMultiFile = new MockMultipartFile("media", file.getName(), null, new FileInputStream(file));
        } catch (IOException e) {
            FileUtil.delFileAndDir(filePath);
            log.error("发送机器人文件时出错,service:" + subject + ",content:" + content);
            return;
        }
        FileUploadResult res = wechatRobotFileUploader.uploadFile(cMultiFile);
        if (res.getMedia_id() == null || "".equals(res.getMedia_id())) {
            log.error("调用出错:" + res);
            FileUtil.delFileAndDir(filePath);
            return;
        }
        Publisher publisher = new Publisher();
        publisher.setMsgtype("file");
        Media media = new Media();
        media.setMedia_id(res.getMedia_id());
        publisher.setFile(media);
        wechatRobotActions.publish(publisher);
        FileUtil.delFileAndDir(filePath);
    }

    private String textFilter(String text) {
        String splitPattern = "(?:Date:\\[)?([0-9]{4}-([0]{1}[1-9]{1}|[1]{1}[0-2]{1})-([0]{1}[1-9]{1}|[1-2]{1}[0-9]{1}|[3]{1}[0-1]{1}) ([0-1]{1}[0-9]{1}|[2]{1}[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1})(.[0-9]{3}])?";
        Pattern stuff = Pattern.compile(splitPattern);
        Matcher matcher = stuff.matcher(text);
        List<String> matchList = new ArrayList<>();
        while (matcher.find()) {
            matchList.add(matcher.group(0)); // add match to the list
        }
        List<String> lists = Arrays.asList(text.split(splitPattern));
        AtomicInteger index = new AtomicInteger(0);
        List<String> graphs = matchList.stream().map(a -> a + lists.get(index.incrementAndGet())).collect(Collectors.toList());
        List<String> filterGraphs = new ArrayList<>();
        for (String graph : graphs) {
            int i = 0;
            for (String logFilterKeyword : logScaner.getCommonFilter()) {
                if (graph.contains(logFilterKeyword)) {
                    break;
                } else {
                    i++;
                }
                if (i == logScaner.getCommonFilter().size()) {
                    filterGraphs.add(graph);
                }
            }
        }
        return String.join("", filterGraphs);
    }

    private List<String> getPoems() {
        ClassPathResource classPathResource = new ClassPathResource("/book/poem.txt");
        String poemStr = "";
        try {
            InputStream poemIn = classPathResource.getInputStream();
            poemStr = IOUtils.toString(poemIn, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Pattern pattern = Pattern.compile("(?<=<p>)[\\s\\S]+?(?=</p>)");
        Matcher matcher = pattern.matcher(poemStr);
        List<String> matchList = new ArrayList<>();
        while (matcher.find()) {
            matchList.add(matcher.group(0));
        }
        return matchList;
    }
}
